package com.routz.wxarticlelist.demo.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import com.routz.wxarticlelist.demo.WxConstant;
import com.routz.wxarticlelist.demo.base_util.Validator;
import com.routz.wxarticlelist.demo.net_util.HttpUtils;
import com.routz.wxarticlelist.demo.net_util.XMLUtil;
import com.routz.wxarticlelist.demo.wx_util.entity.WxConfig;
import com.routz.wxarticlelist.demo.wx_util.entity.WxUserInfo;
import com.routz.wxarticlelist.demo.wx_util.service.IWxUserInfoService;
import com.routz.wxarticlelist.demo.wx_util.service.WxConfigFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Service
public class WxArticleServiceImp implements WxArticleService {
    private static final Logger logger = LoggerFactory.getLogger(WxArticleService.class);
    private static String UrlComponentAccessToken = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";

    /**
     * 推送component_verify_ticket
     * <p>
     * 出于安全考虑，在第三方平台创建审核通过后，
     * 在第三方平台创建审核通过后，微信服务器会向其“授权事件接收URL”每隔10分钟定时推送component_verify_ticket，
     * 用于获取第三方平台接口调用凭据
     * 第三方平台方在收到ticket推送后也需进行解密（详细请见【消息加解密接入指引】）
     * ，接收到后必须直接返回字符串success。
     *
     * @param timestamp
     * @param nonce
     * @param msgSignature
     * @param postData
     */
    @Override
    public void recvTicket(String timestamp, String nonce, String msgSignature, String postData) {
        logger.info("返回的票:" + timestamp + nonce + msgSignature + postData);
        try {
            WXBizMsgCrypt pc = new WXBizMsgCrypt(WxConstant.token, WxConstant.encodingAesKey, WxConstant.appId);
            String result = pc.decryptMsg(msgSignature, timestamp, nonce, postData);
            System.out.println("decryptMsg:\n"+result+"\n\n");
            Map<String, String> xmlToMap = XMLUtil.xmlToMap(result);
            if (!xmlToMap.get("InfoType").equals("component_verify_ticket"))
                throw new Exception("wx没有ticket");
            WxConfig wx = wxConfigFactory.getWxConfig();
            wx.setTicket(xmlToMap.get("ComponentVerifyTicket"));
            wxConfigFactory.setWxConfig(wx);
        } catch (AesException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取第三方平台component_access_token
     * <p>
     * 第三方平台通过自己的component_appid（即在微信开放平台管理中心的第三方平台详情页中的AppID和AppSecret）和
     * component_appsecret，
     * 以及component_verify_ticket（每10分钟推送一次的安全ticket）来获取自己的接口调用凭据（component_access_token）
     */
    @Override
    @Scheduled(fixedDelay = 141 * 60 * 1000L + 51 * 999L)
    public void updateWxComponentAccessToken() {
        logger.info("Scheduled 任务调度 获取access token");
        WxConfig wx = wxConfigFactory.getWxConfig();
        Optional.ofNullable(wx)
                .filter(wxc->wxc != null)
                .orElseThrow(()->new IllegalArgumentException("wx null."));
        String ticket = Validator.verifyText(wx.getTicket());

        HashMap<String, String> map = new HashMap<>();
        map.put("component_appid", WxConstant.appId);
        map.put("component_appsecret", WxConstant.appSecret);
        map.put("component_verify_ticket", ticket);
        String returnDataStr = HttpUtils.post(UrlComponentAccessToken, JSON.toJSONString(map));
        JSONObject returnData = JSONObject.parseObject(returnDataStr);
        String component_access_token = Optional.ofNullable(returnData)
                .filter(json->json.containsKey("component_access_token"))
                .orElseThrow(()->new IllegalArgumentException("JSON解析错误：component_access_token null." + returnData))
                .getString("component_access_token");

        wx.setAccessToken(component_access_token);
        wxConfigFactory.setWxConfig(wx);
    }

    /**
     * 获取预授权码pre_auth_code
     * <p>
     * 第三方平台通过自己的接口调用凭据（component_access_token）
     * 来获取用于授权流程准备的预授权码（pre_auth_code）
     */
    @Override
    @Scheduled(cron="30 0/9 * * * *")
    public void getPreAuthCode() {
        logger.info("获取预授权码");
        WxConfig wxConfig = wxConfigFactory.getWxConfig();
        String url = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token="
                + wxConfig.getAccessToken();
        JSONObject jsonParam = new JSONObject();
        jsonParam.put("component_appid", WxConstant.appId);
        String returnData = HttpUtils.post(url, jsonParam.toString());
        JSONObject resultJson = JSONObject.parseObject(returnData);
        String preAuthCode = Optional.ofNullable(resultJson)
                .filter(json->json.containsKey("pre_auth_code"))
                .orElseThrow(()->new IllegalArgumentException(resultJson.toString()))
                .getString("pre_auth_code");
        wxConfig.setPreAuthCode(preAuthCode);
        wxConfigFactory.setWxConfig(wxConfig);
    }

    /**
     * 进入授权页进行授权
     */
    @Override
    public String getAuthcUrl() {
        return "https://mp.weixin.qq.com/cgi-bin/componentloginpage" +
        "?component_appid=" + WxConstant.appId +
                "&pre_auth_code=" +
                wxConfigFactory.getWxConfig().getPreAuthCode() +
                "&redirect_uri=" + WxConstant.URL + "/wx/authcallback";
    }

    /**
     * 授权回调url
     * <p>
     * 使用授权码换取公众号或小程序的接口调用凭据和授权信息
     *
     * @param auth_code
     * @param expires_in
     * @param userId
     */
    @Override
    public String authCallback(String auth_code, String expires_in, String userId) {
        long l = System.currentTimeMillis() + (Long.parseLong(expires_in) * 1000);
        WxUserInfo wxUserInfoDb = wxUserInfoService.getOne(new LambdaQueryWrapper<WxUserInfo>().eq(WxUserInfo::getUserId, userId));
        if (wxUserInfoDb != null) {
            throw new IllegalArgumentException("用户已绑定");
        }
        WxUserInfo wxUserInfo = new WxUserInfo();
        wxUserInfo.setUserId(userId);
        wxUserInfo.setAuthCode(auth_code);
        wxUserInfo.setAuthCodeExpirationTime(l);
        wxUserInfoService.save(wxUserInfo);
        // 使用授权码换取公众号或小程序的接口调用凭据和授权信息
        this.makeAuthorizerAccessToken(wxUserInfo);

        return WxConstant.URL + "/wx_list.html";
    }

    /**
     * 获取列表
     *
     * @param type
     * @param pageNum
     * @param pageSize
     * @param userId
     */
    @Override
    public Object list(String type, int pageNum, int pageSize, String userId) {
        String authorizerAccessToken = this.getAuthorizerAccessToken(userId);

        String url = "https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token="
                + authorizerAccessToken;
        JSONObject jsonParam = new JSONObject();
        jsonParam.put("type", type);
        jsonParam.put("offset", String.valueOf((pageNum - 1) * pageSize));
        jsonParam.put("count", String.valueOf(pageSize));
        String returnData = HttpUtils.post(url, jsonParam.toString());
//        String data = new String(returnData.getBytes("ISO8859-1"), "UTF-8");
        System.out.println("data:" + returnData);
        JSONObject resultJson = JSONObject.parseObject(returnData);
        Optional.ofNullable(resultJson)
                .filter(json-> null != json.get("item"))
                .orElseThrow(() -> new IllegalArgumentException("wx list result null"));
//        JSONArray item = resultJson.getJSONArray("item");
//        for (int i = 0; i < item.size(); i++) {
//            JSONObject jsonObject = item.getJSONObject(i);
//            JSONArray jsonArray = jsonObject.getJSONObject("content").getJSONArray("news_item");
//            for (int j = 0; j < jsonArray.size(); j++) {
//                JSONObject jo = jsonArray.getJSONObject(j);
//                String mediaId = (String) jo.get("thumb_media_id");
////                WxAuth wxAuthState = wxAuthMapper.findWxAuthState(userId, mediaId);
////                if (wxAuthState == null) {
////                    jo.put("state", 1);         //没确权
////                } else {
////                    jo.put("state", 2);         //已确权
////                }
//            }
//        }
//        PageHelper.startPage(pageNum, pageSize);
        return resultJson;
    }

    /**
     * 获取详情
     *
     * @param userId
     * @param mediaId
     * @return
     */
    @Override
    public Object info(String userId, String mediaId) {
        String authorizerAccessToken = this.getAuthorizerAccessToken(userId);
        String infoUrl = "https://api.weixin.qq.com/cgi-bin/material/get_material?access_token="
                + authorizerAccessToken;

        JSONObject infoJson = new JSONObject();
        infoJson.put("media_id", mediaId);
        String infoData = HttpUtils.post(infoUrl, infoJson.toString());
//        String info = new String(infoData.getBytes("ISO8859-1"), "UTF-8");
//        JSONObject json = JSONObject.fromObject(info);

        return infoData;
    }

    /**********************************************
     *
     * 私有
     *
     **********************************************/
    /**
     * 获取access token
     * @param userId
     * @return
     */
    public String getAuthorizerAccessToken(String userId) {
        // 从数据库查询
        // 若过期了，则刷新
        WxUserInfo wxUserInfo =
                Optional.ofNullable(wxUserInfoService.getOne(new LambdaQueryWrapper<WxUserInfo>().eq(WxUserInfo::getUserId, userId)))
                        .filter(wb->Validator.hasText(wb.getAuthorizerAccessToken()))
                        .filter(wb->wb.getAuthorizerExpiresIn() != null)
                        .orElseThrow(()->new IllegalArgumentException("用户未绑定或没有access token"));
        if (System.currentTimeMillis() < wxUserInfo.getAuthorizerExpiresIn()) {
            // 未过期
            return wxUserInfo.getAuthorizerAccessToken();
        }
        return refreshAuthorizerAccessToken(wxUserInfo);
    }
    /**
     * 首次
     * 获取微信访问access token
     * @param wxUserInfo
     * @return
     */
    public String makeAuthorizerAccessToken(WxUserInfo wxUserInfo) {
        String url = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token="
                + wxConfigFactory.getWxConfig().getAccessToken();
        Validator.verifyText(wxUserInfo.getAuthCode());

        JSONObject jsonParam = new JSONObject();
        jsonParam.put("component_appid", WxConstant.appId);
        jsonParam.put("authorization_code", wxUserInfo.getAuthCode());
        String returnData = HttpUtils.post(url, jsonParam.toString());
        JSONObject resultJson = JSONObject.parseObject(returnData);
        Optional.ofNullable(resultJson)
                .filter(json->json.containsKey("authorization_info"))
                .orElseThrow(()->new IllegalArgumentException("获取access token返回authorization_info null"));
        wxUserInfo.setAuthorizerExpiresIn(System.currentTimeMillis() + (Integer.parseInt(resultJson.getJSONObject("authorization_info").get("expires_in").toString()) * 1000));
        wxUserInfo.setAuthorizerAccessToken(resultJson.getJSONObject("authorization_info").get("authorizer_access_token").toString());
        wxUserInfo.setAuthorizerRefreshToken(resultJson.getJSONObject("authorization_info").get("authorizer_refresh_token").toString());
        wxUserInfo.setAuthorizerAppid(resultJson.getJSONObject("authorization_info").get("authorizer_appid").toString());
        wxUserInfoService.updateById(wxUserInfo);

        return resultJson.getJSONObject("authorization_info").get("authorizer_access_token").toString();
    }

    /**
     * 如果access token过期
     * 刷新accessToken
     *
     * @return
     */
    private String refreshAuthorizerAccessToken(WxUserInfo wxUserInfo) {
        String url = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token="
                + wxConfigFactory.getWxConfig().getAccessToken();
        Optional.ofNullable(wxUserInfo)
                .filter(wui->Validator.hasText(wui.getAuthorizerAppid()))
                .filter(wui->Validator.hasText(wui.getAuthorizerRefreshToken()))
                .orElseThrow(()->new IllegalArgumentException("微信用户appid or refresh token null"));

        JSONObject jsonParam = new JSONObject();
        jsonParam.put("component_appid", WxConstant.appId);
        jsonParam.put("authorizer_appid", wxUserInfo.getAuthorizerAppid());
        jsonParam.put("authorizer_refresh_token", wxUserInfo.getAuthorizerRefreshToken());

        String returnData = HttpUtils.post(url, jsonParam.toString());
        JSONObject resultJson = JSONObject.parseObject(returnData);
        Optional.ofNullable(resultJson)
                .filter(json->Validator.hasText(json.getString("authorizer_access_token")))
                .filter(json->json.containsKey("expires_in"))
        ;
        wxUserInfo.setAuthorizerExpiresIn(System.currentTimeMillis() + (Integer.parseInt(resultJson.get("expires_in").toString()) * 1000));
        wxUserInfo.setAuthorizerAccessToken(resultJson.getString("authorizer_access_token"));
        wxUserInfo.setAuthorizerRefreshToken(resultJson.getString("authorizer_refresh_token"));
        wxUserInfoService.updateById(wxUserInfo);

        return wxUserInfo.getAuthorizerAccessToken();
    }

    @Autowired
    private WxConfigFactory wxConfigFactory;
    @Autowired
    private IWxUserInfoService wxUserInfoService;
}
